.. _глава_71_start:

Выражение is
============

.. index::
    is

Выражение is не связано с оператором is, который мы рассматривали в главе :ref:`NULL значение и оператор is<глава_45_start>`, ни синтаксически, ни семантически:

.. code-block:: d

    a is b // is operator, which we have seen before
    is (/* ... */) // is expression

Выражение is оценивается во время компиляции. Оно выдает значение int, либо 0
или 1, в зависимости от выражения, указанного в круглых скобках. Хотя выражение
выражение, которое оно принимает, не является логическим выражением, само выражение is используется
как логическое выражение во время компиляции. Оно особенно полезно в статических условиях if
и шаблонных ограничениях.
Условие, которое оно принимает, всегда связано с типами, которые должны быть записаны в одном из
из нескольких вариантов

is (T)
------

Определяет, является ли T допустимым типом.
На данный момент сложно придумать примеры для этого использования. Мы воспользуемся
в последующих главах с параметрами шаблонов.

.. code-block:: d

    static if (is(int))
    {
        writeln("valid");
    }
    else
    {
        writeln("invalid");
    }


Вышеприведенный int является допустимым типом:

.. code-block:: console

    valid


В качестве другого примера, поскольку void не является допустимым типом ключа ассоциативного
то ниже будет включен блок else:

.. code-block:: d

    static if (is(string[void]))
    {
        writeln("valid");
    }
    else
    {
        writeln("invalid");
    }

Вывод:

..code-block:: console

    invalid

is (T Alias)
-------------

Работает так же, как и предыдущий синтаксис. Дополнительно определяет Alias как
псевдоним T:

.. code-block:: d

    static if (is (int NewAlias))
    {
        writeln("valid");
        NewAlias var = 42; // int и NewAlias - это одно и то же
    }
    else
    {
        writeln("invalid");
    }

Такие псевдонимы полезны, особенно в более сложных выражениях типа is, как мы увидим
ниже.

is (T : OtherT)
---------------

Определяет, может ли T быть автоматически преобразован в OtherT.
Используется для обнаружения автоматических преобразований типов, которые мы рассматривали в главе
Преобразования типов (:ref:`Преобразования типов<глава_46_start>`), а также отношений типа "этот тип относится к
этого типа", которые мы рассматривали в главе "Наследование" (:ref:`Наследование<глава_55_start>`).

.. code-block:: d

    import std.stdio;

    interface Clock
    {
        void tellTime();
    }

    class AlarmClock : Clock
    {
        override void tellTime()
        {
            writeln("10:00");
        }
    }

    void myFunction(T)(T parameter)
    {
        static if (is(T : Clock))
        {
            // If we are here then T can be used as a Clock
            writeln("This is a Clock; we can tell the time");
            parameter.tellTime();
        }
        else
        {
            writeln("This is not a Clock");
        }
    }

    void main()
    {
        auto var = new AlarmClock;
        myFunction(var);
        myFunction(42);
    }

Когда шаблон myFunction() инстанцируется для типа, который можно использовать как
Clock, то для его параметра вызывается функция-член tellTime().
В противном случае компилируется условие else:

.. code-block:: console

    This is a Clock; we can tell the time ← for AlarmClock
    10:00 ← for AlarmClock
    This is not a Clock ← for int

is (T Alias : OtherT)
----------------------

Работает так же, как и предыдущий синтаксис. Дополнительно определяет Alias как
псевдоним T.


is (T == Specifier)
-------------------
Определяет, является ли T тем же типом, что и Specifier, или соответствует ли T этому
спецификатору.

**Одинаковый ли тип**

Если мы изменим предыдущий пример, используя == вместо :, условие
не будет выполнено для AlarmClock

.. code-block:: d

    static if (is(T == Clock))
    {
        writeln("This is a Clock; we can tell the time");
        parameter.tellTime();
    }
    else
    {
        writeln("This is not a Clock");
    }

Хотя AlarmClock и является часами, он не совсем того же типа, что и Clock. По этой
поэтому теперь условие недействительно как для AlarmClock, так и для int:

.. code-block:: console

    This is not a Clock
    This is not a Clock

**Совпадает ли с одним и тем же спецификатором**

Когда Specifier является одним из следующих ключевых слов, это использование is определяет
соответствует ли тип этому спецификатору (некоторые из этих ключевых слов мы увидим в
последующих главах):

* struct
* union
* class
* interface
* enum
* function
* delegate
* const
* immutable
* shared

.. code-block:: d

    void myFunction(T)(T parameter)
    {
        static if (is(T == class))
        {
            writeln("This is a class type");
        }
        else static if (is(T == enum))
        {
            writeln("This is an enum type");
        }
        else static if (is(T == const))
        {
            writeln("This is a const type");
        }
        else
        {
            writeln("This is some other type");
        }
    }

Шаблоны функций могут воспользоваться этой информацией, чтобы вести себя по-разному
в зависимости от типа, с которым инстанцируется шаблон. Следующий код
демонстрирует, как различные блоки приведенного выше шаблона компилируются для
разных типов:

.. code-block:: d

    auto var = new AlarmClock;
    myFunction(var);
    // (enum WeekDays will be defined below for another example)
    myFunction(WeekDays.Monday);
    const double number = 1.2;
    myFunction(number);
    myFunction(42)

.. code-block:: console

    This is a class type
    This is an enum type
    This is a const type
    This is some other typ

is (T identifier == Specifier)
------------------------------

Работает так же, как и предыдущий синтаксис. идентификатор - это либо псевдоним
типа; либо какая-то другая информация, зависящая от Specifier:

.. list-table:: Права доступа
   :widths: 25 50
   :header-rows: 1
   :align: center

   * - Specifier
     - The meaning of identifier
   * - Struct
     - alias of the type that satisfied the condition
   * - Union
     - alias of the type that satisfied the condition
   * - Class
     - alias of the type that satisfied the condition
   * - Interface
     - alias of the type that satisfied the condition
   * - Super
     - a tuple consisting of the base classes and the interfaces
   * - Enum
     - the actual implementation type of the enum
   * - Function
     - a tuple consisting of the function parameters
   * - delegate
     - the type of the delegate
   * - return
     - the return type of the regular function, the delegate, or the function pointer
   * - __parameters
     - a tuple consisting of the parameters of the regular function, the delegate, or the function pointer
   * - const
     - alias of the type that satisfied the condition
   * - immutable
     - alias of the type that satisfied the condition
   * - shared
     - alias of the type that satisfied the condition

Прежде чем экспериментировать с этим синтаксисом, давайте определим различные типы:

.. code-block:: d

    struct Point
    {
        // ...
    }

    interface Clock
    {
        // ...
    }

    class AlarmClock : Clock
    {
        // ...
    }

    enum WeekDays
    {
        Monday,
        Tuesday,
        Wednesday,
        Thursday,
        Friday,
        Saturday,
        Sunday
    }

    char foo(double d, int i, Clock c)
    {
        return 'a';
    }

В следующем шаблоне функции используются различные спецификаторы с таким синтаксисом выражения
выражение is:

.. code-block:: d

    void myFunction(T)(T parameter)
    {
        static if (is(T LocalAlias == struct))
        {
            writefln("\n--- struct ---");
            // LocalAlias is the same as T. 'parameter' is the
            // struct object that has been passed to this
            // function.
            writefln("Constructing a new %s object by copying it.",
                LocalAlias.stringof);
            LocalAlias theCopy = parameter;
        }
        static if (is(T baseTypes == super))
        {
            writeln("\n--- super ---");
            // The 'baseTypes' tuple contains all of the base
            // types of T. 'parameter' is the class variable that
            // has been passed to this function.
            writefln("class %s has %s base types.",
                T.stringof, baseTypes.length);
            writeln("All of the bases: ", baseTypes.stringof);
            writeln("The topmost base: ", baseTypes[0].stringof);
        }
        static if (is(T ImplT == enum))
        {
            writeln("\n--- enum ---");
            // 'ImplT' is the actual implementation type of this
            //  enum type. 'parameter' is the enum value that has
            //  been passed to this function.
            writefln("The implementation type of enum %s is %s",
                T.stringof, ImplT.stringof);
        }
        static if (is(T ReturnT == return))
        {
            writeln("\n--- return ---");
            // 'ReturnT' is the return type of the function
            // pointer that has been passed to this function.
            writefln("This is a function with a return type of %s:",
                ReturnT.stringof);
            writeln("    ", T.stringof);
            write("calling it... ");
            // Note: Function pointers can be called like
            // functions
            ReturnT result = parameter(1.5, 42, new AlarmClock);
            writefln("and the result is '%s'", result);
        }
    }

Теперь вызовем шаблон этой функции с различными типами, которые мы определили
выше:

.. code-block:: d

    // Calling with a struct object
    myFunction(Point());
    // Calling with a class reference
    myFunction(new AlarmClock);
    // Calling with an enum value
    myFunction(WeekDays.Monday);
    // Calling with a function pointer
    myFunction(&foo);

Вывод:

.. code-block:: d

    --- struct ---
    Constructing a new Point object by copying it.
    --- super ---
    class AlarmClock has 2 base types.
    All of the bases: (Object, Clock)
    The topmost base: Object
    --- enum ---
    The implementation type of enum WeekDays is int
    --- return ---
    This is a function with a return type of char:
    char function(double d, int i, Clock c)
    calling it... and the result is 'a'

is (/\* ... \*/ Specifier, TemplateParamList)
----------------------------------------------

Существует четыре различных синтаксиса выражения is, использующего шаблон
список параметров:

* is (T : Specifier, TemplateParamList)
* is (T == Specifier, TemplateParamList)
* is (T identifier : Specifier, TemplateParamList)
* is (T identifier == Specifier, TemplateParamList)

Эти синтаксисы позволяют использовать более сложные случаи.
identifier, Specifier, : and ==  имеют те же значения, что и описанные выше
выше.
TemplateParamList является как частью условия, которое должно быть выполнено
и средством для определения дополнительных псевдонимов, если условие действительно выполняется. Он
работает так же, как и вычитание типов шаблонов.
В качестве простого примера предположим, что выражение is должно соответствовать
ассоциативным массивам, ключи которых имеют тип string:

.. code-block:: d

    static if (is (T == Value[Key], // (1)
    Value, // (2)
    Key : string)) { // (3)

Это условие можно объяснить тремя частями, две последние из которых являются частями
TemplateParamList:
1. Если T соответствует синтаксису Value[Key]
2. Если Value является типом
3. Если Key - строка (помните синтаксис специализации шаблона (:ref:`Шаблоны<глава_64_start>`)).
Наличие Value[Key] в качестве спецификатора требует, чтобы T был ассоциативным массивом.
Если оставить Value как есть, то это означает, что он может быть любого типа. Кроме того, тип ключа
ассоциативного массива должен быть строковым. В результате предыдущее выражение is означает.
"если T - ассоциативный массив, в котором тип ключа - string".
Следующая программа проверяет это выражение с четырьмя различными типами:

.. code-block:: d

    import std.stdio;

    void myFunction(T)(T parameter)
    {
        writefln("\n--- Called with %s ---", T.stringof);
        static if (is(T == Value[Key],
                Value,
    Key:
                string))
        {
            writeln("Yes, the condition has been satisfied.");
            writeln("The value type: ", Value.stringof);
            writeln("The key type  : ", Key.stringof);
        }
        else
        {
            writeln("No, the condition has not been satisfied.");
        }
    }

    void main()
    {
        int number;
        myFunction(number);
        int[string] intTable;
        myFunction(intTable);
        double[string] doubleTable;
        myFunction(doubleTable);
        dchar[long] dcharTable;
        myFunction(dcharTable);
    }

Условие выполняется, только если тип ключа - строка:

.. code-block:: console

    --- Called with int ---
    No, the condition has not been satisfied.
    --- Called with int[string] ---
    Yes, the condition has been satisfied.
    The value type: int
    The key type  : string
    --- Called with double[string] ---
    Yes, the condition has been satisfied.
    The value type: double
    The key type  : string
    --- Called with dchar[long] ---
    No, the condition has not been satisfied

